package apiv1

import (
	"strconv"

	"gitee.com/djaf/my-certs/app/classes/certificates"
	"gitee.com/djaf/my-certs/app/data/dxo"
	"gitee.com/djaf/my-certs/app/web/dto"
	"gitee.com/djaf/my-certs/app/web/vo"
	"github.com/gin-gonic/gin"
	"github.com/starter-go/libgin"
)

// CsrController ...
type CsrController struct {

	//starter:component
	_as func(libgin.Controller) //starter:as(".")

	Sender  libgin.Responder        //starter:inject("#")
	Service certificates.CSRService //starter:inject("#")
}

func (inst *CsrController) _impl() libgin.Controller {
	return inst
}

// Registration ...
func (inst *CsrController) Registration() *libgin.ControllerRegistration {
	return &libgin.ControllerRegistration{Route: inst.route}
}

func (inst *CsrController) route(rp libgin.RouterProxy) error {

	rp = rp.For("csr")

	rp.POST("", inst.handlePostNew)
	rp.PUT(":id", inst.handlePutVerify)
	rp.GET(":id", inst.handleGetState)
	rp.DELETE(":id", inst.handleDelete)

	return nil
}

func (inst *CsrController) handle(c *gin.Context) {
	req := &myCsrRequest{
		context:       c,
		controller:    inst,
		wantRequestID: false,
	}
	req.execute(req.doNOP)
}

func (inst *CsrController) handleGetState(c *gin.Context) {
	req := &myCsrRequest{
		context:       c,
		controller:    inst,
		wantRequestID: true,
	}
	req.execute(req.doGetState)
}

func (inst *CsrController) handleDelete(c *gin.Context) {
	req := &myCsrRequest{
		context:       c,
		controller:    inst,
		wantRequestID: true,
	}
	req.execute(req.doCancel)
}

func (inst *CsrController) handlePostNew(c *gin.Context) {
	req := &myCsrRequest{
		context:         c,
		controller:      inst,
		wantRequestID:   false,
		wantRequestBody: true,
	}
	req.execute(req.doNew)
}

func (inst *CsrController) handlePutVerify(c *gin.Context) {
	req := &myCsrRequest{
		context:         c,
		controller:      inst,
		wantRequestID:   true,
		wantRequestBody: true,
	}
	req.execute(req.doVerify)
}

////////////////////////////////////////////////////////////////////////////////

type myCsrRequest struct {
	context    *gin.Context
	controller *CsrController

	wantRequestID    bool
	wantRequestQuery bool
	wantRequestBody  bool

	id dxo.CSRID

	body1 vo.CSR
	body2 vo.CSR
}

func (inst *myCsrRequest) open() error {

	c := inst.context

	if inst.wantRequestID {
		idstr := c.Param("id")
		idnum, err := strconv.ParseInt(idstr, 10, 64)
		if err != nil {
			return err
		}
		inst.id = dxo.CSRID(idnum)
	}

	if inst.wantRequestBody {
		err := c.BindJSON(&inst.body1)
		if err != nil {
			return err
		}
	}

	return nil
}

func (inst *myCsrRequest) send(err error) {
	data := &inst.body2
	code := inst.body2.Status
	resp := new(libgin.Response)
	resp.Context = inst.context
	resp.Error = err
	resp.Data = data
	resp.Status = code
	inst.controller.Sender.Send(resp)
}

func (inst *myCsrRequest) execute(fn func() error) {
	err := inst.open()
	if err == nil {
		err = fn()
	}
	inst.send(err)
}

func (inst *myCsrRequest) doNOP() error {
	return nil
}

func (inst *myCsrRequest) doCancel() error {
	ctx := inst.context
	ser := inst.controller.Service
	req := inst.body1.Request
	err := ser.Cancel(ctx, req)
	if err != nil {
		return err
	}
	inst.body2.Request = req
	return nil
}

func (inst *myCsrRequest) doGetState() error {
	ctx := inst.context
	ser := inst.controller.Service
	req := new(dto.CSR)
	req.ID = inst.id
	err := ser.GetState(ctx, req)
	if err != nil {
		return err
	}
	inst.body2.Request = req
	return nil
}

func (inst *myCsrRequest) doNew() error {
	ctx := inst.context
	ser := inst.controller.Service
	req := inst.body1.Request
	err := ser.NewRequest(ctx, req)
	if err != nil {
		return err
	}
	inst.body2.Request = req
	return nil
}

func (inst *myCsrRequest) doVerify() error {
	ctx := inst.context
	ser := inst.controller.Service
	req := inst.body1.Request
	err := ser.UpdateState(ctx, req)
	if err != nil {
		return err
	}
	inst.body2.Request = req
	return nil
}
